/**
 * Copyright (c) 2009 blurredVision
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or 
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 
 * OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "StdAfx.h"
#include "../Component.h"
#include "../DefaultSettings.h"
#include "ResourceFactory.h"

#include <iostream>
#include <algorithm>

namespace gui {

Component* Component::activeComponent = NULL;
IGUIRenderer* Component::pRenderer = NULL;
//IFont* Component::font = NULL;

Component::Component() 
:	iLastButton(-1),
	pParent(NULL),
	vPos(0, 0),
	vRealPos(0, 0),
	vDim(10, 10),
	vOffset(2, 1),
	bMouseEntered(false),
	bVisible(true),
	vBorderColor(DefaultSettings::getBorderColor()),
	vTextColor(DefaultSettings::getTextColor()),
	vBackColor(DefaultSettings::getBackColor()),
	pTexture(NULL),
	pDisabledTexture(NULL),
	bEnabled(true)
{
	pRenderer = getGUIRenderer();
	components.clear();
	vActionListeners.clear();
	vMouseListeners.clear();

	//if (!font)
	font = coreGetResourceFactory()->loadTrueTypeFont("Arial", 16, FONT_NORMAL);	

	iRowSize = font->getSize() + 2;
}

Component::~Component()
{
	clearAll();
	if (activeComponent == this) 
		activeComponent = NULL;
}

void Component::clearAll() 
{
	// Clear all components
	comp_list::iterator it;
	do {
		it = components.begin();
		if (it == components.end()) break;
		DELETE_POINTER( *it);
		it = components.erase( it);
	} while ( it != components.end());
	/*
	for (comp_list::iterator it = components.begin(); it != components.end(); ++it) {
		DELETE_POINTER(*it);
	}
	components.clear();
	*/
	//if (pParent)
	//	pParent->remove(this);

	Component::ExecUpdate();
}


void Component::setDimension(int _x, int _y, int _w, int _h)
{
	setPosition(_x, _y);
	setSize(_w, _h);
	
	Component::ExecUpdate();
}

void Component::centering()
{
	if ( pParent != NULL)
	{
		Component *papa = this->getParent();
		int newX = ( papa->getWidth() - this->getWidth() ) / 2;
		int newY = ( papa->getHeight() - this->getHeight() ) / 2;

		this->setPosition( newX, newY);
	}
	
	Component::ExecUpdate();
}

void Component::setSize(int w, int h)
{
	vDim.x = w;
	vDim.y = h;
	
	Component::ExecUpdate();
}

void Component::setPosition(const math::Vector2i &position)
{
	setPosition(position.x, position.y);
}

const math::Vector2i& Component::getPosition() const
{
	return vPos;
}

const math::Vector2i& Component::getSize() const
{
	return vDim;
}

void Component::setPosition(int _x, int _y)
{
	vPos.x = _x;
	vPos.y = _y;
	
	Component::ExecUpdate();
}

void Component::setBackColor(float r, float g, float b, float a)
{
	vBackColor.x = r; 
	vBackColor.y = g; 
	vBackColor.z = b; 
	vBackColor.w = a;
	
	Component::ExecUpdate();
}

void Component::setBorderColor(float r, float g, float b, float a)
{
	vBorderColor.x = r; 
	vBorderColor.y = g; 
	vBorderColor.z = b; 
	vBorderColor.w = a;
	
	Component::ExecUpdate();
}

void Component::setTextColor(float r, float g, float b, float a)
{
	vTextColor.x = r; 
	vTextColor.y = g; 
	vTextColor.z = b; 
	vTextColor.w = a;
	
	Component::ExecUpdate();
}

void Component::setBackground(ITexture *texture)
{
	pTexture = texture;
	
	Component::ExecUpdate();
}

bool Component::processMouse(int _x, int _y, int _z, int _button)
{
	static MouseEvent me;
	math::Vector2i lastPos;
	m_bActionPerformed = false;

	bool retval = false;

	// Update if visible
	if (bVisible) {
		vRealPos = getAbsolutePosition();

		// Update the mouse event structure
		me.x = _x;
		me.y = _y;
		me.z = _z;
		me.button = _button;
		me.component = this;

		if ( (_x > vRealPos.x && _x < vRealPos.x+vDim.x && 
			 _y > vRealPos.y && _y < vRealPos.y+vDim.y) || iLastButton != -1 ) 
		{
			retval = true;

			// If the mouse entered, send a mouse event
			if (!bMouseEntered)
				onMouseEntered(me);

			// If the mouse button changed, send a mouse pressed / released event
			if (iLastButton != _button) {
				// Update the button
				iLastButton = _button;
				// Send an mouse button event according to the button
				if (iLastButton != -1) {
					onMousePressed(me);
				}
				else {
					onMouseReleased(me);
				}
			}
			// Button has not chaned, but the position has
			if (_button != -1 && (lastPos.x != _x || lastPos.y != _y)) {
				onMouseDragged(me);
			}
			
		} else {
			// If the mouse is leaving, send a mouse event
			if (bMouseEntered)
				onMouseLeft(me);

			// Reset the button
			iLastButton = -1;
		}

		lastPos = math::Vector2i(_x, _y);

		// Always set all the sub components. 
		for (int i=static_cast<int>(components.size())-1; i>=0; --i) {
			if (components.at(i)->processMouse(_x, _y, _z, _button)) {
				return true;
			}
		}
	}
	return retval;
}

void Component::onMouseEntered(const MouseEvent &me)
{
	if (bEnabled) {
		bMouseEntered = true;
		// Pass the event to all registered listeners
		for (mliter = vMouseListeners.begin(); mliter != vMouseListeners.end(); ++mliter)
			(*mliter)->onMouseEnterEvent(me);
		
		Component::ExecUpdate();
	}
}

void Component::onMouseLeft(const MouseEvent &me)
{
	bMouseEntered = false;
	// Pass the event to all registered listeners
	for (mliter = vMouseListeners.begin(); mliter != vMouseListeners.end(); ++mliter)
		(*mliter)->onMouseLeaveEvent(me);

	Component::ExecUpdate();
}

void Component::onMousePressed(const MouseEvent &me)
{
	if (bEnabled) {
		// Set this component as the active component
		if (activeComponent) 
			activeComponent->unsetFocus();	
		activeComponent = this;
		activeComponent->setFocus();

		// Pass the event to all registered listeners
		for (mliter = vMouseListeners.begin(); mliter != vMouseListeners.end(); ++mliter)
			(*mliter)->onMousePressEvent(me);
	}
	Component::ExecUpdate();	
}

void Component::performAction(int x, int y, int button)
{
	static ActionEvent ae;

	// Create and send an action event
	ae.x = x;
	ae.y = y;
	ae.button = button;
	ae.component = this;
	execFunction(ae);
	m_bActionPerformed = true;
}

void Component::onMouseReleased(const MouseEvent &me)
{
	if (bEnabled) {
		// Pass the event to all registered listeners
		for (mliter = vMouseListeners.begin(); mliter != vMouseListeners.end(); ++mliter)
			(*mliter)->onMouseReleaseEvent(me);
	}	
	Component::ExecUpdate();
}

void Component::onMouseDragged(const MouseEvent &me)
{
	if (bEnabled) {
		// Pass the event to all registered listeners
		for (mliter = vMouseListeners.begin(); mliter != vMouseListeners.end(); ++mliter)
			(*mliter)->onMouseDragEvent(me);
	}	
	Component::ExecUpdate();
}

void Component::addActionListener ( IActionListener *actionListener )
{
	if (actionListener != NULL)
		vActionListeners.push_back( actionListener );
}

void Component::removeActionListener( IActionListener *actionListener )
{  
	// Fnd the ActionListener
	al_list::iterator it = std::find(vActionListeners.begin(), vActionListeners.end(), actionListener);
	// Erase the ActionListener
	if (it!=vActionListeners.end())
		vActionListeners.erase(it);
}

void Component::execFunction(const ActionEvent &actionEvent)
{
	if (bEnabled) {
		if (vActionListeners.size() > 0) {
			for (al_list::const_iterator it=vActionListeners.begin(); it != vActionListeners.end(); ++it) 
				(*it)->actionPerformed( actionEvent );
		}
	}
}

void Component::addMouseListener( IMouseListener* mouseListener )
{
	if (mouseListener != NULL)
		vMouseListeners.push_back( mouseListener );
}

void Component::removeMouseListener( IMouseListener* mouseListener )
{
	// Find the MouseListener
	ml_list::iterator it = std::find(vMouseListeners.begin(), vMouseListeners.end(), mouseListener);
	// Erase the MouseListeners
	if (it!=vMouseListeners.end())
		vMouseListeners.erase(it);
}

void Component::add(Component *comp)
{
	comp->setParent(this);
	components.push_back(comp);
	
	Component::ExecUpdate();
}

void Component::remove(Component *comp)
{
	comp->setParent(NULL);
	components.erase( std::find(components.begin(), components.end(), comp) );
	
	Component::ExecUpdate();
}

void Component::bringToFront() 
{
	Component *p = pParent;
	// Component will be moved to the back, so it's drawn last
	p->remove(this);
	p->add(this);
	
	Component::ExecUpdate();
}

void Component::setParent(Component* parent)
{
	// Assign the parent
	pParent = parent;
}

void Component::setVisible(bool value)
{
	bVisible = value;
	bMouseEntered = false;
	Component::ExecUpdate();
}

bool Component::isVisible() const
{
	return bVisible;
}

inline math::Vector2i Component::getAbsolutePosition()
{
	if (!pParent)
		return vPos;
	
	return pParent->getAbsolutePosition() + vPos; 
}

// Windows message handling
void Component::winMsgProc( UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	// Just process, if the gui is visible
	if (bVisible) {
		for (comp_list::const_iterator it=components.begin(); it!=components.end(); ++it)
			(*it)->winMsgProc(uMsg, wParam, lParam);
	}
}

void Component::drawElements()
{
	if (bVisible) {
		comp_list::const_iterator it;
		for (it = components.begin(); it != components.end(); ++it)
			(*it)->draw();
	}
}

gui::Component* Component::getParent()
{
	return pParent;
}

bool Component::s_bDrawChanges = true;

void Component::ExecUpdate()
{
	s_bDrawChanges = true;
}

void Component::setEnabled(bool value)
{
	bEnabled = value;
}

void Component::setDisabledBackground(ITexture *texture)
{
	pDisabledTexture = texture;
}

void Component::setFont(IFont* pFont)
{
	font = pFont;
	iRowSize = font->getSize();
}

} // namespace
